import * as BABYLON from '@babylonjs/core';
import * as GUI from '@babylonjs/gui';
import { BabylonCanvas } from '../BabylonCanvas';
import { TWcsScene, TWcsSceneDevice2d } from '../../models';
import { AbstractDevice } from '../AbstractDevice';
import { Device2D } from './controls/Device2D';
import { Drawer } from './helper/Drawer';
import { Task } from '../../Task';
import { BabylonDevice2d } from './BabylonDevice2d';
import { BaseDeviceControl2d } from './devices/BaseDeviceControl2d';
import { EDirection2d } from './EDirection2d';
import { BabylonScene } from '../BabylonScene';

export class BabylonScene2d extends BabylonScene {
  public container: GUI.Container;
  public scaleMax: number = 3;
  public scaleMin: number = 0.2;
  
  private _pointerDownPoint: BABYLON.Vector2 | null = null;
  private _dragSelected: boolean = false;
  private _dragAll: boolean = false;
  private _showSelector: boolean = false;

  constructor(canvas: BabylonCanvas, tWcsScene: TWcsScene) {
    super(canvas, tWcsScene);
    this.container = new GUI.Container(`${this.id}-container`);
    // this.container.background = '#666666'
    this.container.horizontalAlignment = GUI.Control.HORIZONTAL_ALIGNMENT_CENTER;
    this.container.verticalAlignment = GUI.Control.VERTICAL_ALIGNMENT_CENTER;
    // this.container.clipChildren = false;
    // this.container.clipContent = false;
    this.ui.addControl(this.container);
  }

  public async render(): Promise<void> {
    for (var device of this.device2ds) {
      await device.render();
    }
  }
  public destroy(): Promise<void> {
    throw new Error('Method not implemented.');
  }

  public async init(): Promise<void> {
    await super.init();

    for(var device of this.device2ds) {
      if(device.deviceObject) {
        this.container.addControl(device.deviceObject);
      }
    }
    
    // 重新计算显示画布大小。
    this.resizeContainer(true);

    // 处理鼠标事件，在设计模式下可以选中设备，选中设备后可以设置。
    this.scene.onPointerObservable.add((eventData: BABYLON.PointerInfo) => {
      if (eventData.type == BABYLON.PointerEventTypes.POINTERWHEEL) {
      this.onPointerWheel(eventData);
    } else if (eventData.type == BABYLON.PointerEventTypes.POINTERDOWN) {
      this.onPointerDown(eventData);
    } else if (eventData.type == BABYLON.PointerEventTypes.POINTERUP) {
      this.onPointerUp(eventData);
    } else if (eventData.type == BABYLON.PointerEventTypes.POINTERMOVE) {
      this.onPointerMove(eventData);
    } else if (eventData.type == BABYLON.PointerEventTypes.POINTERDOUBLETAP) {
      this.onPointerDoubleTap(eventData);
    }
    });
  }

  private onPointerWheel(eventData: BABYLON.PointerInfo) {
    var scale = (<WheelEvent>eventData.event).deltaY > 0 ? 0.95 : 1.05  ;
    if (this.container.scaleX * scale > this.scaleMax || this.container.scaleX * scale < this.scaleMin) {
      return;
    }
    this.container.scaleX *= scale;
    this.container.scaleY *= scale;
    this.ui.invalidateRect(0,0,window.screen.width, window.screen.height);
  }

  private onPointerDown(eventData: BABYLON.PointerInfo) {
    if (eventData.event.button == 0) {
      // 判断当前鼠标按下是否在容器中的某个控件上。
      var picked: BabylonDevice2d | null = null;
      var selectedDevices: BabylonDevice2d[] = [];
      for (var device of this.device2ds) {
        if (device.contains(this.scene.pointerX, this.scene.pointerY)) {
          picked = device;
        }

        // 使用 isEnabled 暂时当作选中状态用。
        if (device.deviceObject && device.deviceObject.selected) {
          selectedDevices.push(device);
        }
      }

      // 如果当前点中的设备已经是选中设备，则开启拖动。
      if (picked) {
        if (picked && !selectedDevices.includes(picked, 0)) {
          for (var device of selectedDevices) {
            device.deviceObject!.selected = false;
          }
          picked.deviceObject!.selected = true;
        }

        this._pointerDownPoint = new BABYLON.Vector2(this.scene.pointerX, this.scene.pointerY);
        this._dragSelected = true;
      } else {
        for (var device of selectedDevices) {
          device.deviceObject!.selected = false;
        }

        this._pointerDownPoint = new BABYLON.Vector2(this.scene.pointerX, this.scene.pointerY);
        
        // 建立并显示框选。
        var selector = new GUI.Rectangle("__kr__selector");
        selector.verticalAlignment = GUI.Control.VERTICAL_ALIGNMENT_TOP;
        selector.horizontalAlignment = GUI.Control.HORIZONTAL_ALIGNMENT_LEFT;
        selector.left = this._pointerDownPoint.x + "px";
        selector.top = this._pointerDownPoint.y + "px";
        selector.width = 1 + "px";
        selector.height = 1 + "px";
        selector.background = "#85c1e9";
        selector.thickness = 1;
        selector.color = "#000080";
        selector.alpha = 0.3;
        this.container.parent!.addControl(selector);
        this._showSelector = true;
      }
    } else if (eventData.event.button == 2) {
      this._pointerDownPoint = new BABYLON.Vector2(this.scene.pointerX, this.scene.pointerY);
      this.device2ds.forEach(tt => {if(tt.deviceObject) {tt.deviceObject.isVisible = true}});
      this._dragAll = true;
    }
  }

  private onPointerMove(eventData: BABYLON.PointerInfo) {
    if (this._dragSelected && this._pointerDownPoint) {
      var selectedDevices: BabylonDevice2d[] = [];
      for (var device of this.device2ds) {
        if (device.deviceObject && device.deviceObject.selected) {
          selectedDevices.push(device);
        }
      }

      if (selectedDevices.length > 0) {
        // 这里需要考虑将所有已选中的控件全部放入一个容器中以方便操作（同时效率会不会高点？）
        var movedx = (this.scene.pointerX - this._pointerDownPoint.x) / this.container.scaleX;
        var movedy = (this.scene.pointerY - this._pointerDownPoint.y) / this.container.scaleY;
        this._pointerDownPoint = new BABYLON.Vector2(
          this.scene.pointerX,
          this.scene.pointerY
        );

        for (var device of selectedDevices) {
          if(device.deviceObject) {
            var newX = device.deviceObject.x + movedx;
            var newY = device.deviceObject.y + movedy;
            device.deviceObject.x = newX;
            device.deviceObject.y = newY;
          }
        }
      }

      this.resizeContainer();
    } else if (this._dragAll && this._pointerDownPoint) {
      var movedx = this.scene.pointerX - this._pointerDownPoint.x;
      var movedy = this.scene.pointerY - this._pointerDownPoint.y;
      this._pointerDownPoint = new BABYLON.Vector2(
        this.scene.pointerX,
        this.scene.pointerY
      );
      this.container.left = this.container.leftInPixels + movedx;
      this.container.top = this.container.topInPixels + movedy;
    } else if (this._showSelector && this._pointerDownPoint) {
      var selector = this.container.parent?.getChildByName("__kr__selector");
      if (selector) {
        const distancx = this.scene.pointerX - this._pointerDownPoint.x;
        const distancy = this.scene.pointerY - this._pointerDownPoint.y;
        const selectorX = distancx < 0 ? this.scene.pointerX : this._pointerDownPoint.x;
        const selectorY = distancy < 0 ? this.scene.pointerY : this._pointerDownPoint.y;
        const selectorPoint =new BABYLON.Vector2(selectorX, selectorY);
        const x = selectorPoint.x + "px";
        const y = selectorPoint.y + "px";
        const w = Math.abs(distancx) + "px";
        const h = Math.abs(distancy) + "px";
        selector.left = x;
        selector.top = y;
        selector.width = w;
        selector.height = h;
      }
    }
  }

  private onPointerUp(eventData: BABYLON.PointerInfo) {
    if (this._showSelector) {
      var selector = this.container.parent?.getChildByName("__kr__selector");
      if (selector) {
        for (var device of this.device2ds) {
          if(device.deviceObject) {
            device.deviceObject.selected = this.isCovered(selector as GUI.Rectangle, device.deviceObject)
          }
        }

        this.container.parent!.removeControl(selector);
      }
    }
    if (this._pointerDownPoint) {
      this._pointerDownPoint = null;
      this._dragAll = false;
      this._dragSelected = false;
      this._showSelector = false;
    }
  }

  private onPointerDoubleTap(eventData: BABYLON.PointerInfo) {
    alert('double clicked' + eventData.event.button);
  }

  private isCovered(selector: GUI.Rectangle, control: BaseDeviceControl2d): boolean {
    var x = control.transformedMeasure.left;
    var y = control.transformedMeasure.top;
    var w = control.transformedMeasure.width;
    var h = control.transformedMeasure.height;
    return selector.contains(x, y);
  }

  private resizeContainer(resetCenter: boolean = false) {
    var minx = 0, miny = 0, maxx = 0, maxy = 0;
    for(var device of this.device2ds) {
      if(!device.deviceObject) {
        continue;
      }

      var x = device.deviceObject.x;
      var y = device.deviceObject.y;
      var w = device.deviceObject.w;
      var h = device.deviceObject.h;
      minx = Math.min(minx, x);
      miny = Math.min(miny, y);
      maxx = Math.max(maxx, x + w);
      maxy = Math.max(maxy, y + h);

      const tx = device.deviceObject.transformedMeasure.left;
      const ty = device.deviceObject.transformedMeasure.top;
      const tw = w * this.container.scaleX;
      const th = h * this.container.scaleY;
      const uiW = this.ui.getSize().width;
      const uiH = this.ui.getSize().height;
      device.deviceObject.isVisible = device.deviceObject.selected 
        || (tx >= 0 && tx <= uiW && ty >= 0 && ty <= uiH)
        || (tx + tw >= 0 && tx + tw <= uiW && ty + th >= 0 && ty + th <= uiH)
        || (tx >= 0 && tx + tw <= uiW && ty >= 0 && ty + th <= uiH)
        || (tx + tw >= 0 && tx <= uiW && ty + th >= 0 && ty <= uiH);
    }
    
    var newW = Math.max(Math.abs(minx), Math.abs(maxx)) * 3;
    var newH = Math.max(Math.abs(miny), Math.abs(maxy)) * 3;
    if(this.container.widthInPixels < newW || this.container.heightInPixels < newH) {
      this.container.width = newW + 'px';
      this.container.height = newH + 'px';
      if(resetCenter) {
        this.container.left = this.container.leftInPixels + this.container.centerX - (maxx - minx) / 2 + 'px';
        this.container.top = this.container.topInPixels + this.container.centerY - (maxy - miny) / 2 + 'px';
      }
    }
  }
}
